package com.vita.bduck.logger.helps;

import java.util.HashMap;
import java.util.Map;

/**
 * 根据简单的替换规则，格式化消息,替换可以1，2个或多个参数。
 * <p/>
 * <p/>
 * 例如
 *
 * <p/>
 * <pre>
 * MessageFormatter.format(&quot;Hi {}.&quot;, &quot;there&quot;)
 * </pre>
 * result : "Hi threre."
 *
 * <pre>
 * MessageFormatter.format(&quot;Set {1,2,3} is not equal to {}.&quot;, &quot;1,2&quot;);
 * </pre>
 * result : "Set {1,2,3} is not equal to 1,2.".
 *
 * <pre>
 * MessageFormatter.format(&quot;Set \\{} is not equal to {}.&quot;, &quot;1,2&quot;);
 * </pre>
 *
 * result： "Set {} is not equal to 1,2.".
 *
 * <pre>
 * MessageFormatter.format(&quot;File name is C:\\\\{}.&quot;, &quot;file.zip&quot;);
 * </pre>
 *
 * result : "File name is C:\file.zip".
 *
 * Created with IntelliJ IDEA.
 * User: Vita Hao
 * Date: 13-6-19
 * Time: 上午10:08
 */
final public class MessageFormatter {
    private static final String TAG = "MessageFormatter";
    static final char DELIM_START = '{';
    static final char DELIM_STOP = '}';
    static final String DELIM_STR = "{}";
    private static final char ESCAPE_CHAR = '\\';


    /**
     * 执行单参数格式化，messagePattern为格式规则
     * @param messagePattern  消息模式将被解析和格式化
     * @param arg 替换位置格式化的可变参数
     * @return
     */
    final public static FormattingTuple format(String messagePattern,Object arg){
        return arrayFormat(messagePattern, new Object[] { arg });
    }

    /**
     * 执行双格式化，messagePattern为格式规则参数
     *
     * For example,
     *
     * <pre>
     * MessageFormatter.format(&quot;Hi {}. My name is {}.&quot;, &quot;Alice&quot;, &quot;Bob&quot;);
     * </pre>
     *
     *  "Hi Alice. My name is Bob.".
     *
     * @param messagePattern
     * @param arg1
     * @param arg2
     * @return
     */
    final public static FormattingTuple format(final String messagePattern,
                                               Object arg1, Object arg2) {
        return arrayFormat(messagePattern, new Object[] { arg1, arg2 });
    }

    /**
     * 执行参数格式化，messagePattern为格式规则
     * @param messagePattern
     * @param argArray
     * @return
     */
    final public static FormattingTuple arrayFormat(String messagePattern, final Object[] argArray) {
        Throwable throwableCandidate = getThrowableCandidate(argArray);

        if (messagePattern == null) {
            return new FormattingTuple(null, argArray, throwableCandidate);
        }

        if (argArray == null) {
            return new FormattingTuple(messagePattern);
        }

        int i = 0;
        int j;
        StringBuffer  sbuf = new StringBuffer(messagePattern.length() + 50);//规则字符串+50，预留额外

        int L;
        for (L = 0; L < argArray.length; L++) {

            j = messagePattern.indexOf(DELIM_STR, i);//如果包含"{}"，i累计字符串的开始读取位置

            if (j == -1) {
                // 不包含
                if (i == 0) { // 是普通的字符串
                    return new FormattingTuple(messagePattern, argArray,
                            throwableCandidate);
                } else { // 尾部添加字符串，其中包含没有变量和返回结果。
                    sbuf.append(messagePattern.substring(i, messagePattern.length()));
                    return new FormattingTuple(sbuf.toString(), argArray,
                            throwableCandidate);
                }
            } else {//判断字符串是否有"\\"
                if (isEscapedDelimeter(messagePattern, j)) {
                    if (!isDoubleEscaped(messagePattern, j)) {
                        L--; //DELIM_START去除，因此不应该被加
                        sbuf.append(messagePattern.substring(i, j - 1));//去除'\\'
                        sbuf.append(DELIM_START);
                        i = j + 1;
                    } else {
                       // 转义字符分隔 开始前本身要去除 ：“abc x：\\{}” ，要消耗一个反斜杠
                        sbuf.append(messagePattern.substring(i, j - 1));
                        deeplyAppendParameter(sbuf, argArray[L], new HashMap());//新建一个Map
                        i = j + 2;
                    }
                } else {
                    // 正常情况下
                    sbuf.append(messagePattern.substring(i, j));
                    deeplyAppendParameter(sbuf, argArray[L], new HashMap());//新建一个Map
                    i = j + 2;
                }
            }
        }
        // append the characters following the last {} pair.
        sbuf.append(messagePattern.substring(i, messagePattern.length()));
        if (L < argArray.length - 1) {
            return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
        } else {
            return new FormattingTuple(sbuf.toString(), argArray, null);
        }

    }

    /**
     * 如果是一个‘\\’
     * @param messagePattern
     * @param delimeterStartIndex 开始的index
     * @return
     */
    final static boolean isEscapedDelimeter(String messagePattern,
                                            int delimeterStartIndex) {

        if (delimeterStartIndex == 0) {
            return false;
        }
        char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1);
        if (potentialEscape == ESCAPE_CHAR) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 如果是两个‘\\’
     * @param messagePattern
     * @param delimeterStartIndex  开始的index
     * @return
     */
    final static boolean isDoubleEscaped(String messagePattern,
                                         int delimeterStartIndex) {
        if (delimeterStartIndex >= 2
                && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 判断对象数组是Throwable类
     * @param argArray
     * @return
     */
    static final Throwable getThrowableCandidate(Object[] argArray) {
        if (argArray == null || argArray.length == 0) {
            return null;
        }

        final Object lastEntry = argArray[argArray.length - 1];
        if (lastEntry instanceof Throwable) {
            return (Throwable) lastEntry;
        }
        return null;
    }


    /**
     * 特殊处理数组，往深层追加参数到字符串
     * @param sbuf
     * @param o
     * @param seenMap
     */
    private static void deeplyAppendParameter(StringBuffer sbuf,Object o,Map seenMap){
        if(o == null){
            sbuf.append("null");
            return;
        }

        //如果不是数组
        if(!o.getClass().isArray()){
            safeObjectAppend(sbuf,o);
        }else{
            //检查为原始数组类型，因为不能转换Object[]
            if (o instanceof boolean[]) {
                booleanArrayAppend(sbuf, (boolean[]) o);
            } else if (o instanceof byte[]) {
                byteArrayAppend(sbuf, (byte[]) o);
            } else if (o instanceof char[]) {
                charArrayAppend(sbuf, (char[]) o);
            } else if (o instanceof short[]) {
                shortArrayAppend(sbuf, (short[]) o);
            } else if (o instanceof int[]) {
                intArrayAppend(sbuf, (int[]) o);
            } else if (o instanceof long[]) {
                longArrayAppend(sbuf, (long[]) o);
            } else if (o instanceof float[]) {
                floatArrayAppend(sbuf, (float[]) o);
            } else if (o instanceof double[]) {
                doubleArrayAppend(sbuf, (double[]) o);
            } else {
                objectArrayAppend(sbuf, (Object[]) o, seenMap);
            }
        }
    }

    /**
     * 把对象的toString 加入到字符串
     * @param sbuf
     * @param o
     */
    private static void safeObjectAppend(StringBuffer sbuf, Object o) {
        try {
            String oAsString = o.toString();
            sbuf.append(oAsString);
        } catch (Throwable t) {
            android.util.Log.e(TAG,"Failed toString() invocation on an object of type ["
                    + o.getClass().getName() + "]");
            t.printStackTrace();
            sbuf.append("[FAILED toString()]");
        }
    }

    /**
     * 从对象数组追加到字符串
     * @param sbuf
     * @param a
     * @param seenMap
     */
    private static void objectArrayAppend(StringBuffer sbuf, Object[] a,
                                          Map seenMap) {
        sbuf.append('[');
        if (!seenMap.containsKey(a)) {//如果对象数组在map中
            seenMap.put(a, null);
            final int len = a.length;
            for (int i = 0; i < len; i++) {
                deeplyAppendParameter(sbuf, a[i], seenMap);
                if (i != len - 1)
                    sbuf.append(", ");
            }
            //追加完后就从map中删除
            seenMap.remove(a);
        } else {
            sbuf.append("...");
        }
        sbuf.append(']');
    }


    /**
     * 将boolean[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void booleanArrayAppend(StringBuffer sbuf, boolean[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }


    /**
     * 将byte[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void byteArrayAppend(StringBuffer sbuf, byte[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }


    /**
     * 将char[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void charArrayAppend(StringBuffer sbuf, char[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }


    /**
     * 将short[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void shortArrayAppend(StringBuffer sbuf, short[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }

    /**
     * 将int[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void intArrayAppend(StringBuffer sbuf, int[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }

    /**
     * 将long[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void longArrayAppend(StringBuffer sbuf, long[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }

    /**
     * 将float[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void floatArrayAppend(StringBuffer sbuf, float[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }

    /**
     * 将double[] 加入字符串
     * @param sbuf
     * @param a
     */
    private static void doubleArrayAppend(StringBuffer sbuf, double[] a) {
        sbuf.append('[');
        final int len = a.length;
        for (int i = 0; i < len; i++) {
            sbuf.append(a[i]);
            if (i != len - 1)
                sbuf.append(", ");
        }
        sbuf.append(']');
    }
}
